#pragma once

#include "dev_assert.h"

namespace cal
{
// * 因为 原Token_stream::peek() 会将下一个 token 放入 buffer 中，如果 buffer
// * 只能装一个，那么以下代码会报错
// *    peek();   putback(token);
// * 因此建立此类在逻辑上保留 buffer 只有一个，但是 不会发生上述报错
// * 使用了此缓冲区运行逻辑：_buffer 永远保存流中第一个 token (if !full_buffer: peek 为第一个 token)
template <typename T> class Buffer
{
public:
    Buffer() : _buffer{}, _peek{} {}

public:
    void clear_buffer() 
    { 
        _buffer = T{}; 
        CAL_DEV_ASSERT(_buffer.empty());
    }

    void clear_peek() 
    { 
        _peek = T{}; 
        CAL_DEV_ASSERT(_peek.empty());
    }

    void clear()
    {
        clear_buffer();
        clear_peek();
    }
    
    T peek() { return (full_buffer()) ? _buffer : _peek; }
    T get()
    {
        T t;
        if (full_buffer()) {
            t       = _buffer;
            clear_buffer();
        }
        else {
            t     = _peek;
            clear_peek();
        }
        return t;
    }

    void putback(const T& t)
    {
        push_buffer(t);
    }

    void push_buffer(const T& t)
    {
        CAL_DEV_ASSERT_MESSAGE(!full_buffer(), "buffer is full_buffer");
        _buffer = t;
    }

    void push_peek(const T& t)
    {
        CAL_DEV_ASSERT_MESSAGE(empty_peek(), "peek is full_buffer");
        _peek = t;
    }
    
    bool full_buffer() const { return !_buffer.empty(); }
    bool empty_peek() const { return _peek.empty(); }
    bool empty() const { return (!full_buffer()) && empty_peek(); }

private:
    /**
     * _buffer: 用于存放 putback (实际的缓冲区)
     * _peek: 用于存放 peek 刷入缓存区的token
     */
    T _buffer;
    T _peek;
};

}   // namespace cal